// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System.Data;
using JetBrains.Annotations;
using SnapObjects.Commons;

namespace SnapObjects.Data
{
    /// <summary>
    ///     For DataTable, imports or exports data in XML/Json format.
    /// </summary>
    public static class DataTableExtensions
    {
        #region DataPacker Extensions
        /// <summary>
        ///     Add a DataTable object as an element to the IDataPacker object.
        /// </summary>
        /// <param name="packer"></param>
        /// <param name="key">The key for the element, which is the identifier of the element in the IDataPacker object.</param>
        /// <param name="value">A DataTable object, which is the value of the element to be added to the IDataPacker object.</param>
        public static void AddDataTable(
           [NotNull]this IDataPacker packer,
           [NotNull]string key,
           [NotNull]DataTable value)
        {
            Check.NotNull(packer, nameof(packer));
            Check.NotNull(key, nameof(key));
            Check.NotNull(value, nameof(value));

            packer.AddDataTable(key, value, false);
        }

        /// <summary>
        ///     Add a DataTable object as an element to the IDataPacker object, and specifies whether to export only 
        ///     the changed data from the DataTable object.
        /// </summary>
        /// <param name="packer"></param>
        /// <param name="key">The key for the element, which is the identifier of the element in the IDataPacker object.</param>
        /// <param name="value">The DataTable object, which is the value of the element to be added to the IDataPacker object.</param>
        /// <param name="changedOnly">
        ///     True -- to export the changed rows in the DataTable object only (the row state is in DataRowState.Added, 
        ///     DataRowState.Deleted or DataRowState.Modified).
        ///     False -- to export all rows in the DataTable object.
        /// </param>
        public static void AddDataTable(
           [NotNull]this IDataPacker packer,
           [NotNull]string key,
           [NotNull]DataTable value,
           [NotNull]bool changedOnly)
        {
            Check.NotNull(packer, nameof(packer));
            Check.NotNull(key, nameof(key));
            Check.NotNull(value, nameof(value));

            ((DataPacker)packer).AddFormater(key, new DataTablePackerFormater(value, changedOnly));
        }

        /// <summary>
        ///     Gets the DataTable object created by the specified element.
        /// </summary>
        /// <param name="dataPacker"></param>
        /// <param name="key">The key specifying which element will be used to create the DataTable object.</param>
        /// <returns>Returns the newly created DataTable object.</returns>
        public static DataTable GetDataTable(
            [NotNull]this IDataPacker dataPacker,
            [NotNull]string key)
        {
            Check.NotNull(dataPacker, nameof(dataPacker));
            Check.NotNull(key, nameof(key));

            var dataTable = new DataTable();

            var importer = new DataTableImporter(dataTable);

            importer.Import(dataPacker.GetRaw(key), dataPacker.DataFormat);

            return dataTable;
        }

        /// <summary>
        ///     Gets the DataTable object created by the specified element.
        /// </summary>
        /// <param name="dataUnPackage"></param>
        /// <param name="key">The key specifying which element will be used to create the DataTable object.</param>
        /// <returns>Returns the newly created DataTable object.</returns>
        public static DataTable GetDataTable(
            [NotNull]this IDataUnpacker dataUnPackage,
            [NotNull]string key)
        {
            Check.NotNull(dataUnPackage, nameof(dataUnPackage));
            Check.NotNull(key, nameof(key));

            var dataTable = new DataTable();

            var importer = new DataTableImporter(dataTable);

            importer.Import(dataUnPackage.GetRaw(key), dataUnPackage.DataFormat);

            return dataTable;
        }
        #endregion

        #region ImportJson
        /// <summary>
        ///     Imports data from a JSON string into the DataTable.
        /// </summary>
        /// <param name="dataTable"></param>
        /// <param name="json">
        ///     A string specifying the JSON data.
        ///     The JSON string must comply with the Plain JSON or DataWindow JSON.
        /// </param>
        /// <returns>Returns the number of rows that were imported if it succeeds.</returns>
        public static int ImportJson(this DataTable dataTable, string json)
        {
            return ImportJson(dataTable, json, -1);
        }

        /// <summary>
        ///     Imports data from a JSON string into the DataTable, and specifies the starting position of the rows.
        /// </summary>
        /// <param name="dataTable"></param>
        /// <param name="json">
        ///     A string specifying the JSON data.
        ///     The JSON string must comply with the Plain JSON or DataWindow JSON.
        /// </param>
        /// <param name="startRow">The zero-based index number of the first row in the buffer that you want to export.</param>
        /// <returns>Returns the number of rows that were imported if it succeeds.</returns>
        public static int ImportJson(this DataTable dataTable, string json, int startRow)
        {
            return ImportJson(dataTable, json, startRow, -1);
        }

        /// <summary>
        ///     Imports data from a JSON string into the DataTable, and specifies the starting position of the rows.
        /// </summary>
        /// <param name="dataTable"></param>
        /// <param name="json">
        ///     A string specifying the JSON data.
        ///     The JSON string must comply with the Plain JSON or DataWindow JSON.
        /// </param>
        /// <param name="startRow">The zero-based index number of the first row in the buffer that you want to import.</param>
        /// <param name="endRow">The zero-based index number of the last row in the buffer that you want to export.</param>
        /// <returns>Returns the number of rows that were imported if it succeeds.</returns>
        public static int ImportJson(this DataTable dataTable, string json, int startRow, int endRow)
        {
            return ImportJson(dataTable, json, startRow, endRow, -1);
        }

        /// <summary>
        ///     Imports data from a JSON string into the DataTable, and specifies the starting position of the rows.
        /// </summary>
        /// <param name="dataTable"></param>
        /// <param name="json">
        ///     A string specifying the JSON data.
        ///     The JSON string must comply with the Plain JSON or DataWindow JSON.
        /// </param>
        /// <param name="startRow">The zero-based index number of the first row in the buffer that you want to import.</param>
        /// <param name="endRow">The zero-based index number of the last row in the buffer that you want to import.</param>
        /// <param name="startProperty">The zero-based index number of the first property in the buffer that you want to import.</param>
        /// <returns>Returns the number of rows that were imported if it succeeds.</returns>
        public static int ImportJson(this DataTable dataTable, string json, int startRow, int endRow, short startProperty)
        {
            return ImportJson(dataTable, json, startRow, endRow, startProperty, -1);
        }

        /// <summary>
        ///     Imports data from a JSON string into the DataTable, and specifies the starting position of the rows.
        /// </summary>
        /// <param name="dataTable"></param>
        /// <param name="json">
        ///     A string specifying the JSON data.
        ///     The JSON string must comply with the Plain JSON or DataWindow JSON.
        /// </param>
        /// <param name="startRow">The zero-based index number of the first row in the buffer that you want to import.</param>
        /// <param name="endRow">The zero-based index number of the last row in the buffer that you want to import.</param>
        /// <param name="startProperty">The zero-based index number of the first property in the buffer that you want to import.</param>
        /// <param name="endProperty">The zero-based index number of the last property in the buffer that you want to import.</param>
        /// <returns>Returns the number of rows that were imported if it succeeds.</returns>
        public static int ImportJson(
            this DataTable dataTable, string json, int startRow, int endRow, short startProperty, short endProperty)
        {
            var importer = new DataTableJsonImporter(dataTable);

            return importer.Import(json, startRow, endRow, startProperty, endProperty);
        }

        /// <summary>
        ///     Imports data from the JSON string into the DataTable based on the key name.
        /// </summary>
        /// <param name="dataTable"></param>
        /// <param name="json">
        ///     A string specifying the JSON data.
        ///     The JSON string must comply with the Plain JSON or DataWindow JSON.
        /// </param>
        /// <returns>Returns the number of rows that were imported if it succeeds.</returns>
        public static int ImportJsonByKey(this DataTable dataTable, string json)
        {
            return ImportJsonByKey(dataTable, json, -1);
        }

        /// <summary>
        ///     Imports data from the JSON string into the DataTable based on the key name.
        /// </summary>
        /// <param name="dataTable"></param>
        /// <param name="json">
        ///     A string specifying the JSON data.
        ///     The JSON string must comply with the Plain JSON or DataWindow JSON.
        /// </param>
        /// <param name="startRow">The zero-based index number of the first row in the buffer that you want to import.</param>
        /// <returns>Returns the number of rows that were imported if it succeeds.</returns>
        public static int ImportJsonByKey(this DataTable dataTable, string json, int startRow)
        {
            return ImportJsonByKey(dataTable, json, startRow, -1);
        }

        /// <summary>
        ///     Imports data from the JSON string into the DataTable based on the key name.
        /// </summary>
        /// <param name="dataTable"></param>
        /// <param name="json">
        ///     A string specifying the JSON data.
        ///     The JSON string must comply with the Plain JSON or DataWindow JSON.
        /// </param>
        /// <param name="startRow">The zero-based index number of the first row in the buffer that you want to import.</param>
        /// <param name="endRow">The zero-based index number of the last row in the buffer that you want to import.</param>
        /// <returns>Returns the number of rows that were imported if it succeeds.</returns>
        public static int ImportJsonByKey(this DataTable dataTable, string json, int startRow, int endRow)
        {
            var importer = new DataTableJsonImporter(dataTable);

            return importer.ImportByKey(json, startRow, endRow);
        }

        #endregion

        #region ExportJson
        /// <summary>
        ///     Exports all rows or only the changed rows from the DataTable to a Plain JSON string.
        /// </summary>
        /// <param name="dataTable"></param>
        /// <param name="changedOnly">
        ///     A boolean specifying the changing flag.
        ///     True: to export the changed rows only(and all rows in the delete buffer).
        ///     False(default) : to export all rows.
        /// </param>
        /// <returns>Returns the JSON string.</returns>
        public static string ExportJson(this DataTable dataTable, bool changedOnly)
        {
            var exporter = new DataTableJsonExporter(dataTable);

            return exporter.ExportPlain(changedOnly);
        }

        /// <summary>
        ///     Exports all rows or only the changed rows from the DataTable to a Plain JSON string.
        /// </summary>
        /// <param name="dataTable"></param>
        /// <returns>Returns the JSON string.</returns>
        public static string ExportJson(this DataTable dataTable)
        {
            return ExportJson(dataTable, -1);
        }

        /// <summary>
        ///     Exports data from the DataTable to a Plain JSON string, and specifies the starting position of the rows.
        /// </summary>
        /// <param name="dataTable"></param>
        /// <param name="startRow">The zero-based index number of the first row in the buffer that you want to export.</param>
        /// <returns>Returns the JSON string.</returns>
        public static string ExportJson(this DataTable dataTable, int startRow)
        {
            return ExportJson(dataTable, startRow, -1);
        }

        /// <summary>
        ///     Exports data from the DataTable to a Plain JSON string, and specifies the starting position of the rows.
        /// </summary>
        /// <param name="dataTable"></param>
        /// <param name="startRow">The zero-based index number of the first row in the buffer that you want to export.</param>
        /// <param name="endRow">The zero-based index number of the last row in the buffer that you want to export.</param>
        /// <returns>Returns the JSON string.</returns>
        public static string ExportJson(this DataTable dataTable, int startRow, int endRow)
        {
            return ExportJson(dataTable, startRow, endRow, -1);
        }

        /// <summary>
        ///     Exports data from the DataTable to a Plain JSON string, and specifies the starting position of the rows.
        /// </summary>
        /// <param name="dataTable"></param>
        /// <param name="startRow">The zero-based index number of the first row in the buffer that you want to export.</param>
        /// <param name="endRow">The zero-based index number of the last row in the buffer that you want to export.</param>
        /// <param name="startColumn">The zero-based index number of the first column in the buffer that you want to export.</param>
        /// <returns>Returns the JSON string.</returns>
        public static string ExportJson(this DataTable dataTable, int startRow, int endRow, short startColumn)
        {
            return ExportJson(dataTable, startRow, endRow, startColumn, -1);
        }

        /// <summary>
        ///     Exports data from the DataTable to a Plain JSON string, and specifies the starting position of the rows.
        /// </summary>
        /// <param name="dataTable"></param>
        /// <param name="startRow">The zero-based index number of the first row in the buffer that you want to export.</param>
        /// <param name="endRow">The zero-based index number of the last row in the buffer that you want to export.</param>
        /// <param name="startColumn">The zero-based index number of the first column in the buffer that you want to export.</param>
        /// <param name="endColumn">The zero-based index number of the last column in the buffer that you want to export.</param>
        /// <returns>Returns the JSON string.</returns>
        public static string ExportJson(
            this DataTable dataTable, int startRow, int endRow, short startColumn, short endColumn)
        {
            var exporter = new DataTableJsonExporter(dataTable);

            return exporter.ExportPlain(startRow, endRow, startColumn, endColumn);
        }

        #endregion

        #region ImportXml
        /// <summary>
        ///     Imports data from a XML string into the DataTable.
        /// </summary>
        /// <param name="dataTable"></param>
        /// <param name="xml">
        ///     A string specifying the Xml data.
        ///     The xml string must comply with the Plain Xml or DataWindow Xml.
        /// </param>
        /// <returns>Returns the number of rows that were imported if it succeeds.</returns>
        public static int ImportXml(this DataTable dataTable, string xml)
        {
            return ImportXml(dataTable, xml, -1);
        }

        /// <summary>
        ///     Imports data from a XML string into the DataTable.
        /// </summary>
        /// <param name="dataTable"></param>
        /// <param name="xml">
        ///     A string specifying the Xml data.
        ///     The xml string must comply with the Plain Xml or DataWindow Xml.
        /// </param>
        /// <param name="startRow">The zero-based index number of the first row in the buffer that you want to import.</param>
        /// <returns>Returns the number of rows that were imported if it succeeds.</returns>
        public static int ImportXml(this DataTable dataTable, string xml, int startRow)
        {
            return ImportXml(dataTable, xml, startRow, -1);
        }

        /// <summary>
        ///     Imports data from a XML string into the DataTable.
        /// </summary>
        /// <param name="dataTable"></param>
        /// <param name="xml">
        ///     A string specifying the Xml data.
        ///     The xml string must comply with the Plain Xml or DataWindow Xml.
        /// </param>
        /// <param name="startRow">The zero-based index number of the first row in the buffer that you want to import.</param>
        /// <param name="endRow">The zero-based index number of the last row in the buffer that you want to import.</param>
        /// <returns>Returns the number of rows that were imported if it succeeds.</returns>
        public static int ImportXml(this DataTable dataTable, string xml, int startRow, int endRow)
        {
            return ImportXml(dataTable, xml, startRow, endRow, -1);
        }

        /// <summary>
        ///     Imports data from a XML string into the DataTable.
        /// </summary>
        /// <param name="dataTable"></param>
        /// <param name="xml">
        ///     A string specifying the Xml data.
        ///     The xml string must comply with the Plain Xml or DataWindow Xml.
        /// </param>
        /// <param name="startRow">The zero-based index number of the first row in the buffer that you want to import.</param>
        /// <param name="endRow">The zero-based index number of the last row in the buffer that you want to import.</param>
        /// <param name="startProperty">The zero-based index number of the first property in the buffer that you want to import.</param>
        /// <returns>Returns the number of rows that were imported if it succeeds.</returns>
        public static int ImportXml(this DataTable dataTable, string xml, int startRow, int endRow, short startProperty)
        {
            return ImportXml(dataTable, xml, startRow, endRow, startProperty, -1);
        }

        /// <summary>
        ///     Imports data from a XML string into the DataTable.
        /// </summary>
        /// <param name="dataTable"></param>
        /// <param name="xml">
        ///     A string specifying the Xml data.
        ///     The xml string must comply with the Plain Xml or DataWindow Xml.
        /// </param>
        /// <param name="startRow">The zero-based index number of the first row in the buffer that you want to import.</param>
        /// <param name="endRow">The zero-based index number of the last row in the buffer that you want to import.</param>
        /// <param name="startProperty">The zero-based index number of the first property in the buffer that you want to import.</param>
        /// <param name="endProperty">The zero-based index number of the last property in the buffer that you want to import.</param>
        /// <returns>Returns the number of rows that were imported if it succeeds.</returns>
        public static int ImportXml(
            this DataTable dataTable, string xml, int startRow, int endRow, short startProperty, short endProperty)
        {
            var importer = new DataTableXmlImporter(dataTable);

            return importer.Import(xml, startRow, endRow, startProperty, endProperty);
        }

        /// <summary>
        ///     Imports data from the XML string into the DataTable based on the key name, 
        ///     and specifies the start and end positions of the rows.
        /// </summary>
        /// <param name="dataTable"></param>
        /// <param name="xml">
        ///     A string specifying the Xml data.
        ///     The xml string must comply with the Plain Xml or DataWindow Xml .
        /// </param>
        /// <returns>Returns the number of rows that were imported if it succeeds.</returns>
        public static int ImportXmlByKey(this DataTable dataTable, string xml)
        {
            return ImportXmlByKey(dataTable, xml, -1);
        }

        /// <summary>
        ///     Imports data from the XML string into the DataTable based on the key name, 
        ///     and specifies the start and end positions of the rows.
        /// </summary>
        /// <param name="dataTable"></param>
        /// <param name="xml">
        ///     A string specifying the Xml data.
        ///     The xml string must comply with the Plain Xml or DataWindow Xml .
        /// </param>
        /// <param name="startRow">The zero-based index number of the first row in the buffer that you want to import.</param>
        /// <returns>Returns the number of rows that were imported if it succeeds.</returns>
        public static int ImportXmlByKey(this DataTable dataTable, string xml, int startRow)
        {
            return ImportXmlByKey(dataTable, xml, startRow, -1);
        }

        /// <summary>
        ///     Imports data from the XML string into the DataTable based on the key name, 
        ///     and specifies the start and end positions of the rows.
        /// </summary>
        /// <param name="dataTable"></param>
        /// <param name="xml">
        ///     A string specifying the Xml data.
        ///     The xml string must comply with the Plain Xml or DataWindow Xml .
        /// </param>
        /// <param name="startRow">The zero-based index number of the first row in the buffer that you want to import.</param>
        /// <param name="endRow">The zero-based index number of the last row in the buffer that you want to import.</param>
        /// <returns>Returns the number of rows that were imported if it succeeds.</returns>
        public static int ImportXmlByKey(this DataTable dataTable, string xml, int startRow, int endRow)
        {
            var importer = new DataTableXmlImporter(dataTable);

            return importer.ImportByKey(xml, startRow, endRow);
        }

        #endregion

        #region ExportXml

        /// <summary>
        ///     Exports all rows or only the changed rows from the DataTable to a Plain Xml string.
        /// </summary>
        /// <param name="dataTable"></param>
        /// <param name="changedOnly">
        ///     A boolean specifying the changing flag.
        ///     True: to export the changed rows only(and all rows in the delete buffer).
        ///     False(default) : to export all rows.
        /// </param>
        /// <returns></returns>
        public static string ExportXml(this DataTable dataTable, bool changedOnly)
        {
            var exporter = new DataTableXmlExporter(dataTable);

            return exporter.ExportPlain(changedOnly);
        }

        /// <summary>
        ///     Exports data from the DataTable to a Plain Xml string, and specifies the 
        ///     start and end positions of the rows and columns.
        /// </summary>
        /// <param name="dataTable"></param>
        /// <returns>Returns the XML string.</returns>
        public static string ExportXml(this DataTable dataTable)
        {
            return ExportXml(dataTable, -1);
        }

        /// <summary>
        ///     Exports data from the DataTable to a Plain Xml string, and specifies the 
        ///     start and end positions of the rows and columns.
        /// </summary>
        /// <param name="dataTable"></param>
        /// <param name="startRow">The zero-based index number of the first row in the buffer that you want to export.</param>
        /// <returns>Returns the XML string.</returns>
        public static string ExportXml(this DataTable dataTable, int startRow)
        {
            return ExportXml(dataTable, startRow, -1);
        }

        /// <summary>
        ///     Exports data from the DataTable to a Plain Xml string, and specifies the 
        ///     start and end positions of the rows and columns.
        /// </summary>
        /// <param name="dataTable"></param>
        /// <param name="startRow">The zero-based index number of the first row in the buffer that you want to export.</param>
        /// <param name="endRow">The zero-based index number of the last row in the buffer that you want to export.</param>
        /// <returns>Returns the XML string.</returns>
        public static string ExportXml(this DataTable dataTable, int startRow, int endRow)
        {
            return ExportXml(dataTable, startRow, endRow, -1);
        }

        /// <summary>
        ///     Exports data from the DataTable to a Plain Xml string, and specifies the 
        ///     start and end positions of the rows and columns.
        /// </summary>
        /// <param name="dataTable"></param>
        /// <param name="startRow">The zero-based index number of the first row in the buffer that you want to export.</param>
        /// <param name="endRow">The zero-based index number of the last row in the buffer that you want to export.</param>
        /// <param name="startColumn">The zero-based index number of the first column in the buffer that you want to export.</param>
        /// <returns>Returns the XML string.</returns>
        public static string ExportXml(this DataTable dataTable, int startRow, int endRow, short startColumn)
        {
            return ExportXml(dataTable, startRow, endRow, startColumn, -1);
        }

        /// <summary>
        ///     Exports data from the DataTable to a Plain Xml string, and specifies the 
        ///     start and end positions of the rows and columns.
        /// </summary>
        /// <param name="dataTable"></param>
        /// <param name="startRow">The zero-based index number of the first row in the buffer that you want to export.</param>
        /// <param name="endRow">The zero-based index number of the last row in the buffer that you want to export.</param>
        /// <param name="startColumn">The zero-based index number of the first column in the buffer that you want to export.</param>
        /// <param name="endColumn">The zero-based index number of the last column in the buffer that you want to export.</param>
        /// <returns>Returns the XML string.</returns>
        public static string ExportXml(
            this DataTable dataTable, int startRow, int endRow, short startColumn, short endColumn)
        {
            var exporter = new DataTableXmlExporter(dataTable);

            return exporter.ExportPlain(startRow, endRow, startColumn, endColumn);
        }

        #endregion
    }
}
